<?php 

/**
 * 测试的基类
 * 自己写的小测试类，自动完成以test开头的方法调用
 *
 * @author  九九
 * @version 1.0
 * @date    2011.08.17
 */


class XUnitTest {
    public $totalTests;             //总的测试个数

    protected $_totalAsserts;       //总的断言测试个数
    protected $_totalOkTests;       //总的成功测试个数
    protected $_totalFailTests;     //总的错误测试个数
    protected $_totalOtherTests;    //总的没有断言的测试个数
    protected $_startTime;          //程序开始时间
    protected $_endTime;            //程序结束时间
    protected $_totalTime;          //程序总用时
    
    protected static $timeZone          = 'Asia/ShangHai';  //设置当前时区
    protected static $parentClassName   = 'XUnitTest';      //设置测试的父类

    /**
     * 构造函数 
     *
     * @param null
     * @access public
     * @return void
     */
    public function __construct() {
        $this->__toString();
        $this->initConfig();
        $this->totalTests       = 0;
        $this->_totalAsserts    = 0;
        $this->_totalOkTests    = 0;
        $this->_totalFailTests  = 0;
        $this->_totalOtherTests = 0;
        $this->_endTime         = 0;
        $this->_totalTime       = 0;
        $this->_startTime       = self::getNowTime();
        $this->setup();
    }
    
    /**
     * 输出这类是用来做啥
     *
     * @param null
     * @access public
     * @return void
     */ 
    public function __toString() {
       //print("\n---- XUnitTest 1.0 只为少写几行重复代码而存在：D ----\n\n"); 
       print("\n---- XUnitTest 1.0 只为做好TDD而存在：D ----\n\n"); 
    }

    /**
     * 程序结束，看看要销毁些什么
     *
     * @param null
     * @access public
     * @return void
     */
    public function __destruct() {
        $this->destroy(); 
        $this->genTimeInfo();
        $this->getTotalOtherTests();
        $this->printTestInfo();
    }

    /**
     * 初始化配置信息
     * 
     * @param null
     * @access protected
     * @return void
     */
    protected function initConfig() {
        date_default_timezone_set(self::$timeZone);
    }


    /**
     * 在构造函数之后，子类准备一些什么东西
     *
     * @param null
     * @access protected
     * @return void
     */
    protected function setup() {
        //some code
    }

    /**
     * 在析构函数里，子类里去实现销毁些什么
     *
     * @param null
     * @access protected
     * @return void
     */
    protected function destroy() {
        //some code 
    }
    
    /**
     * 生成运行时间信息
     *
     * @param null
     * @access protected
     * @return void
     */
    protected function genTimeInfo() {
        $this->_endTime     = self::getNowTime(); 
        $this->_totalTime   = $this->_endTime - $this->_startTime;
    }

    /**
     * 得到总的无断言测试用例个数
     *
     * @param null
     * @access protected
     * @return void
     */
    protected function getTotalOtherTests() {
        $this->_totalOtherTests     = $this->totalTests - ($this->_totalOkTests + $this->_totalFailTests);
        if($this->_totalOtherTests < 0) {
            $this->_totalOtherTests = 0;
        }
    }
    
    /**
     * 判断，期望值是否跟结果相等
     *
     * @param mix $expect 断言
     * @param mix $result 测试结果
     * @access protected
     * @return void
     */
    protected function assertEquals($expect = '', $result) {
        if(is_array($expect) && is_array($result)) {
            $arrDiff    = $this->compareArrayDiff($expect, $result);
            if(empty($arrDiff)) {
                $this->_totalOkTests ++;
                $this->printTestOkInfo($result);
            } else {
                $this->_totalFailTests ++;
                $this->printTestArrayFailInfo($expect, $result, $arrDiff);
            }
        } else {
            if($expect  === $result) {
                $this->_totalOkTests ++;
                $this->printTestOkInfo($result);
            } else {
                $this->_totalFailTests ++;
                $this->printTestFailInfo($expect, $result);
            }
        }
        $this->_totalAsserts ++;
    }

    /**
     * 通过array_diff来比较两个数组是否相等
     *
     * @param array $arrExp
     * @param array $arrRes
     * @access protected
     * @return array 
     */
    protected function compareArrayDiff($arrExp, $arrRes) {
        $arrDiff   = array_diff($arrRes, $arrExp);
        if(empty($arrDiff)) {
            if(count($arrExp) != count($arrRes)) {
               return array('exp', array_diff($arrExp, $arrRes)); 
            }
            
            return $arrDiff; 
        }

        return array('res', $arrDiff);
    }

    //比较两个数组是不是相等
    protected function compareArray($arrExp, $arrRes) {
        $arrComp    = array_uintersect($arrExp, $arrRes, "compareValue");
        if(count($arrComp) == count($arrRes)) {
           return true; 
        }

        return false;
    }

    //比较两值是不是全等 
    public function compareValue($expValue, $resValue) {
        if( $expValue   === $resValue ) {
            return 0;
        }

        return -1;
    }

    /**
     * 结果是不是真
     *
     * @param mix $result 测试结果
     * @access protected
     * @return void
     */
    protected function assertIsTrue($result) {
        if(true === $result) {
            $this->_totalOkTests ++;
            $this->printTestOkInfo($result);
        } else {
            $this->_totalFailTests ++;
            $this->printTestFailInfo($expect, $result);
        }
        $this->_totalAsserts ++;
    }
    
    /**
     * 程序开始时间 $startTime settor
     *
     * @param double $startTime
     * @access public
     * @return void
     */
    public function setStartTime($startTime) {
        $this->_startTime   = $startTime;
    }

    /**
     * 程序开始时间 $startTime gettor
     *
     * @param null
     * @access public
     * @return double
     */
    public function getStartTime() {
        return $this->_startTime;
    }

    /**
     * 程序结束时间 $endTime settor
     *
     * @param double $endTime
     * @access public
     * @return void
     */
    public function setEndTime($endTime) {
        $this->_endTime     = $endTime;
    }

    /**
     * 程序结束时间 $endTime gettor
     *
     * @param null
     * @access public
     * @return double 
     */
    public function getEndTime() {
        return $this->_endTime;
    }

    /**
     * 得到类里所有可用的方法
     *
     * @param null
     * @access public
     * @return array 类里可用的方法
     */
    public function getClassMethods() {
        return get_class_methods($this);
    }


    /**
     * 打印正确测试用例结果信息
     *
     * @param mix $result 测试结果
     * @access protected
     * @return void
     */
    protected function printTestOkInfo($result) {
        print("[ :) ] -- OK\n结果：");
        var_dump($result);
    }

    /**
     * 打印错误测试用例结果信息
     *
     * @param mix $expect 断言
     * @param mix $result 测试结果
     * @access protected
     * @return void
     */
    protected function printTestFailInfo($expect, $result) {
       print("[ :( ] -- Faill !!!!\n期望值：");
       var_dump($expect);
       print("结果是：");
       var_dump($result);
    }
    
    /**
     * 打印错误测试用例结果信息
     *
     * @param mix $expect 断言
     * @param mix $result 测试结果
     * @param array $arrDiff 比较结果中的不同项
     * @access protected
     * @return void
     */
    protected function printTestArrayFailInfo($expect, $result, $arrDiff) {
        $this->printTestFailInfo($expect, $result); 
        if($arrDiff[0] == 'res') {
            printf("结果中不同项：");
        } else {
            printf("期待数组中不同项：");
        }
        var_dump($arrDiff[1]);
    }
    
    /**
     * 输出测试结果信息
     *
     * @param null
     * @access protected
     * @return void
     */
    protected function printTestInfo() {
        if($this->totalTests == 0) {
            print("[Warning] 此类里没有可用的测试用例，请确认。:D\n\n");
        } else {
            print("\n一共进行 " . $this->totalTests . ' 测试。' . 
                  "Asserts：{$this->_totalAsserts} 次。" .
                  "OK：{$this->_totalOkTests} 次；" .
                  "Fails：{$this->_totalFailTests} 次；" .
                  "No Assert：{$this->_totalOtherTests} 次。\n");
            print("一共用时：{$this->_totalTime} 毫秒。\n");
            print("一共使用内存：" . $this->getMemoryUsage() . " 。\n");
        } 
    }
   
    /**
     * 输出当前所执行的测试用例
     *
     * @param string $method 测试方法名
     * @access public
     * @return void
     */
    public function printRunTimeInfo($method = '') {
        print("{{$this->totalTests}} 测试用例：" . $method . "。\n");
    }
 
    /**
     * 得到程序使用的总内存
     *
     * @param char $memoryUnit 内存单位,默认为 M 兆
     * @access protected
     * @return string 内存结果信息 
     */
    protected function getMemoryUsage($memoryUnit = 'M') {
        $bytes  = memory_get_usage();
        switch($memoryUnit) {
        case 'B':
            return $bytes . 'Byte';
            break;
        case 'K':
            return ($bytes / 1000) . 'KB';
            break;
        case 'M':
            return ($bytes / 1000000) . 'M';
            break;
        default:
            return $bytes . 'Byte';
            break;
        }
    }

    /**
     * 测试用例开始跑,入口函数
     *
     * @param null
     * @access public static
     * @return void
     */
    public static function run() {
        $instance   = null;
        $classes    = get_declared_classes();
        foreach($classes as $className) {
            if(is_subclass_of($className, self::$parentClassName)) {
                $instance   = new $className();
                $methods    = $instance->getClassMethods();
                foreach($methods as $method) {
                    if(preg_match('/^test\w+/i',$method,$result)) {
                        $instance->totalTests++;
                        $instance->printRunTimeInfo($method);
                        $instance->$method();
                    }
                }
            }
        }
    }

    /**
     * 得到当前的时间单位毫秒
     *
     * @param null
     * @access public static
     * @reutrn double
     */
    public static function getNowTime() {
        $microTime    = microtime();
        $microTime    = explode(' ', $microTime);
        $microTime    = $microTime[0] + $microTime[1];
        
        return $microTime;
    }

}

//比较两值是不是全等 
function compareValue($expValue, $resValue) {
    if( $expValue   === $resValue ) {
        return 0;
    }

    return -1;
}

?>
